﻿using System;
using System.Collections.Generic;
using System.Configuration;
using System.Data;
using System.Data.SqlClient;
using System.IO;
using System.Linq;
using System.Net;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading;
using HtmlAgilityPack;

namespace CollectHongKongCompanies
{
    public class RequestController
    {
        private const string PathOfCompanyID = "/html[1]/body[1]/div[2]/table[3]/tr[1]/td[1]/table[1]/tr[1]/td[2]/font[1]";
        private const string PathOfCompanyName = "/html[1]/body[1]/div[2]/table[3]/tr[1]/td[1]/table[1]/tr[2]/td[2]/span[1]";
        private const string PathOfCompanyType = "/html[1]/body[1]/div[2]/table[3]/tr[1]/td[1]/table[1]/tr[3]/td[2]/font[1]";
        private const string PathOfIncorporationDate = "/html[1]/body[1]/div[2]/table[3]/tr[1]/td[1]/table[1]/tr[4]/td[2]/font[1]";
        private const string PathOfCompanyStatus = "/html[1]/body[1]/div[2]/table[3]/tr[1]/td[1]/table[1]/tr[5]/td[2]/font[1]";
        private const string PathOfActiveStatus = "/html[1]/body[1]/div[2]/table[3]/tr[1]/td[1]/table[1]/tr[6]/td[2]/font[1]";
        private const string PathOfRemarks = "/html[1]/body[1]/div[2]/table[3]/tr[1]/td[1]/table[1]/tr[7]/td[2]/font[1]";
        private const string PathOfWindingUpMode = "/html[1]/body[1]/div[2]/table[3]/tr[1]/td[1]/table[1]/tr[8]/td[2]/font[1]";
        private const string PathOfDissolutionDate = "/html[1]/body[1]/div[2]/table[3]/tr[1]/td[1]/table[1]/tr[9]/td[2]/font[1]";
        private const string PathOfRegisterOfCharges = "/html[1]/body[1]/div[2]/table[3]/tr[1]/td[1]/table[1]/tr[10]/td[2]/font[1]";
        private const string PathOfImportantNote = "/html[1]/body[1]/div[2]/table[3]/tr[1]/td[1]/table[1]/tr[11]/td[2]/font[1]";

        public void SendRequest()
        {
            var cc = new CookieContainer();

            var url = "https://www.icris.cr.gov.hk/csci/login_i.do?loginType=iguest&username=iguest";
            var req = (HttpWebRequest)WebRequest.Create(new Uri(url));
            req.CookieContainer = cc;

            var res = (HttpWebResponse)req.GetResponse();

            // Check Network
            if (res.StatusCode != HttpStatusCode.OK)
            {
                Console.WriteLine("Network Error!");
                return;
            }

            res.Close();

            var companies = GetCompanyIDs();

            if((companies == null) || (companies.Count() == 0))
            {
                Console.WriteLine("沒有需要采集的公司编号.");
                return;
            }

            foreach (string companyID in companies)
            {
                var company = GetCompanyInfo(cc, companyID);
                if (company != null)
                {
                    SaveCompanyInfo(company);

                    UpdateSeeder(companyID);

                    Console.WriteLine("收集公司'{0}'信息完毕.", company.CompanyName);

                    Thread.Sleep(2000);
                }
            }
        }

        private CompanyInfo GetCompanyInfo(CookieContainer cc, string companyID)
        {
            string url;
            HttpWebRequest req;
            HttpWebResponse res;
            StreamReader sr;
            string str;

            url = "https://www.icris.cr.gov.hk/csci/cps_criteria.jsp";
            req = (HttpWebRequest)WebRequest.Create(new Uri(url));
            req.CookieContainer = cc;

            res = (HttpWebResponse)req.GetResponse();
            res.Close();

            url = "https://www.icris.cr.gov.hk/csci/cps_criteria.do";
            req = (HttpWebRequest)WebRequest.Create(new Uri(url));
            req.CookieContainer = cc;

            var postData = @"nextAction=cps_criteria&searchPage=True&DPDSInd=true&" +
                           "searchMode=BYCRNO&radioButton=BYCRNO&CRNo=" + companyID + "&" +
                           "mode=EXACT+NAME&showMedium=true&companyName=&language=en&page=1";

            byte[] data = Encoding.UTF8.GetBytes(postData);

            req.Method = "POST";
            req.ContentType = "application/x-www-form-urlencoded";
            req.ContentLength = data.Length;
            req.Accept = "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8";

            var newStream = req.GetRequestStream();

            // Send the data.  
            newStream.Write(data, 0, data.Length);
            newStream.Close();

            res = (HttpWebResponse)req.GetResponse();

            if (res.StatusCode == HttpStatusCode.OK)
            {
                sr = new StreamReader(res.GetResponseStream(), Encoding.UTF8);
                str = sr.ReadToEnd();
                sr.Close();
            }
            else
            {
                Console.WriteLine("Network Error!");
                return null;
            }

            res.Close();

            if (str.Contains("NO MATCHING RECORD FOUND FOR THE SEARCH INFORMATION INPUT!"))
            {
                return null;
            }

            var doc = new HtmlDocument();
            doc.LoadHtml(str);
            var company = new CompanyInfo();
            var rootNode = doc.DocumentNode;
            company.CompanyID = RepaceTag(rootNode.SelectSingleNode(PathOfCompanyID).InnerText);
            company.CompanyName = RepaceTag(rootNode.SelectSingleNode(PathOfCompanyName).InnerText);
            company.CompanyType = RepaceTag(rootNode.SelectSingleNode(PathOfCompanyType).InnerText);

            if (!string.IsNullOrEmpty(RepaceTag(rootNode.SelectSingleNode(PathOfIncorporationDate).InnerText)))
            {
                company.IncorporationDate = Convert.ToDateTime(RepaceTag(rootNode.SelectSingleNode(PathOfIncorporationDate).InnerText));
            }
            
            company.CompanyStatus = RepaceTag(rootNode.SelectSingleNode(PathOfCompanyStatus).InnerText);
            company.ActiveStatus = RepaceTag(rootNode.SelectSingleNode(PathOfActiveStatus).InnerText);
            company.Remarks = RepaceTag(rootNode.SelectSingleNode(PathOfRemarks).InnerText);
            company.WindingUpMode = RepaceTag(rootNode.SelectSingleNode(PathOfWindingUpMode).InnerText);
            if (!string.IsNullOrEmpty(RepaceTag(rootNode.SelectSingleNode(PathOfDissolutionDate).InnerText)))
            {
                company.DissolutionDate = Convert.ToDateTime(RepaceTag(rootNode.SelectSingleNode(PathOfDissolutionDate).InnerText));
            }
            company.RegisterOfCharges = RepaceTag(rootNode.SelectSingleNode(PathOfRegisterOfCharges).InnerText);
            company.ImportantNote = RepaceTag(rootNode.SelectSingleNode(PathOfImportantNote).InnerText);

            return company;
        }

        private string RepaceTag(string text)
        {
            var content = Regex.Replace(text, @"[\r\n\t]", string.Empty).Trim();

            if (content == "-")
            {
                return string.Empty;
            }

            return content;
        }

        private IEnumerable<string> GetCompanyIDs()
        {
            var companyIDOfStart = ConfigurationManager.AppSettings["CompanyIDOfStart"].Trim();

            if(companyIDOfStart.Length != 7)
            {
                throw new Exception("config文件配置不正确，起始公司编号长度不为7");
            }

            var companyIDOfFinish = ConfigurationManager.AppSettings["CompanyIDOfFinish"].Trim();

            if (companyIDOfFinish.Length != 7)
            {
                throw new Exception("config文件配置不正确，结束公司编号长度不为7");
            }

            var collectSequence = ConfigurationManager.AppSettings["CollectSequence"].Trim();

            if (collectSequence.Length > 4)
            {
                throw new Exception("config文件配置不正确，采集排序长度不正确");
            }

            string sql = @"select top " + ConfigurationManager.AppSettings["CollectCount"] + " CompanyID from seeder where UpdateDate is null and companyID > " + companyIDOfStart + " and companyID < " + companyIDOfFinish + " order by companyID " + collectSequence;

            using (var sqlconn = new SqlConnection(ConfigurationManager.ConnectionStrings["HongKongCompanies"].ConnectionString))
            {
                using (var sqlcmd = new SqlCommand(sql, sqlconn))
                {
                    try
                    {
                        List<string> companies = new List<string>();
                        sqlcmd.Connection.Open();
                        SqlDataReader sqldr = sqlcmd.ExecuteReader();
                        while (sqldr.Read())
                        {
                            companies.Add(sqldr["CompanyID"].ToString());
                        }

                        return companies;
                    }

                    finally
                    {
                        sqlcmd.Connection.Close();
                    }
                }
            }
        }

        private void SaveCompanyInfo(CompanyInfo company)
        {
            const string sql = @"insert into CompanyInfo (CompanyID,CompanyName,CompanyType,
                    IncorporationDate,CompanyStatus,ActiveStatus,Remarks,WindingUpMode,DissolutionDate,RegisterOfCharges,
                    ImportantNote) values (@CompanyID,@CompanyName,@CompanyType,@IncorporationDate,@CompanyStatus,
                    @ActiveStatus,@Remarks,@WindingUpMode,@DissolutionDate,@RegisterOfCharges,@ImportantNote)";

            using (var sqlconn = new SqlConnection(ConfigurationManager.ConnectionStrings["HongKongCompanies"].ConnectionString))
            {
                using (var sqlcmd = new SqlCommand(sql, sqlconn))
                {
                    try
                    {
                        sqlcmd.Parameters.Add("@CompanyID", SqlDbType.Char, 7);
                        sqlcmd.Parameters["@CompanyID"].Value = company.CompanyID;

                        sqlcmd.Parameters.Add("@CompanyName", SqlDbType.NVarChar, 512);
                        sqlcmd.Parameters["@CompanyName"].Value = company.CompanyName;

                        sqlcmd.Parameters.Add("@CompanyType", SqlDbType.NVarChar, 128);
                        sqlcmd.Parameters["@CompanyType"].Value = company.CompanyType;

                        if (company.IncorporationDate != DateTime.MinValue)
                        {
                            sqlcmd.Parameters.Add("@IncorporationDate", SqlDbType.DateTime);
                            sqlcmd.Parameters["@IncorporationDate"].Value = company.IncorporationDate;
                        }
                        else
                        {
                            sqlcmd.Parameters.Add("@IncorporationDate", SqlDbType.DateTime);
                            sqlcmd.Parameters["@IncorporationDate"].Value = DBNull.Value;
                        }

                        sqlcmd.Parameters.Add("@CompanyStatus", SqlDbType.NVarChar, 128);
                        sqlcmd.Parameters["@CompanyStatus"].Value = company.CompanyStatus;

                        sqlcmd.Parameters.Add("@ActiveStatus", SqlDbType.NVarChar, 128);
                        sqlcmd.Parameters["@ActiveStatus"].Value = company.ActiveStatus;

                        sqlcmd.Parameters.Add("@Remarks", SqlDbType.NVarChar, 1024);
                        sqlcmd.Parameters["@Remarks"].Value = company.Remarks;

                        sqlcmd.Parameters.Add("@WindingUpMode", SqlDbType.NVarChar, 256);
                        sqlcmd.Parameters["@WindingUpMode"].Value = company.WindingUpMode;


                        if (company.DissolutionDate != DateTime.MinValue)
                        {
                            sqlcmd.Parameters.Add("@DissolutionDate", SqlDbType.DateTime);
                            sqlcmd.Parameters["@DissolutionDate"].Value = company.DissolutionDate;
                        }
                        else
                        {
                            sqlcmd.Parameters.Add("@DissolutionDate", SqlDbType.DateTime);
                            sqlcmd.Parameters["@DissolutionDate"].Value = DBNull.Value;
                        }

                        sqlcmd.Parameters.Add("@RegisterOfCharges", SqlDbType.NVarChar, 128);
                        sqlcmd.Parameters["@RegisterOfCharges"].Value = company.RegisterOfCharges;

                        sqlcmd.Parameters.Add("@ImportantNote", SqlDbType.NVarChar, 512);
                        sqlcmd.Parameters["@ImportantNote"].Value = company.ImportantNote;

                        sqlcmd.Connection.Open();
                        sqlcmd.ExecuteNonQuery();
                    }
                    finally
                    {
                        sqlcmd.Connection.Close();
                    }
                }
            }
        }

        private void UpdateSeeder(string companyID)
        {
            const string sql = "update Seeder set UpdateDate=GETDATE() where CompanyID=@CompanyID;";

            using (var sqlconn = new SqlConnection(ConfigurationManager.ConnectionStrings["HongKongCompanies"].ConnectionString))
            {
                using (var sqlcmd = new SqlCommand(sql, sqlconn))
                {
                    try
                    {
                        sqlcmd.Parameters.Add("@CompanyID", SqlDbType.Char, 7);
                        sqlcmd.Parameters["@CompanyID"].Value = companyID;

                        sqlcmd.Connection.Open();
                        sqlcmd.ExecuteNonQuery();
                    }
                    finally
                    {
                        sqlcmd.Connection.Close();
                    }
                }
            }
        }

    }
}
